using System;
using System.Collections.Generic;
using System.IO;
using NFreeMarker.Template;
using NFreeMarker.Template.Utility;

namespace NFreeMarker.Core
{
    /**
     * A holder for builtins that operate exclusively on strings.
     */
    internal abstract class StringBuiltins
    {
        internal abstract class StringBuiltIn : BuiltIn
        {
            protected override ITemplateModel _getAsTemplateModel(Environment env)
            {
                return CalculateResult(Target.GetStringValue(env), env);
            }

            protected abstract ITemplateModel CalculateResult(string s, Environment env);
        }

        internal class CapitalizeBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(StringUtil.Capitalize(s));
            }
        }

        internal class ChopLinebreakBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(StringUtil.Chomp(s));
            }
        }

        internal class JStringBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(StringUtil.JavaStringEnc(s));
            }
        }

        internal class JsStringBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(StringUtil.JavaScriptStringEnc(s));
            }
        }

        internal class CapFirstBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                int i = 0;
                int ln = s.Length;
                while (i < ln && char.IsWhiteSpace(s[i]))
                {
                    i++;
                }
                if (i < ln)
                {
                    char[] b = s.ToCharArray();
                    b[i] = char.ToUpper(s[i]);
                    s = new string(b);
                }
                return new SimpleScalar(s);
            }
        }

        internal class UncapFirstBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                int i = 0;
                int ln = s.Length;
                while (i < ln && char.IsWhiteSpace(s[i]))
                {
                    i++;
                }
                if (i < ln)
                {
                    char[] b = s.ToCharArray();
                    b[i] = char.ToLower(s[i]);
                    s = new string(b);
                }
                return new SimpleScalar(s);
            }
        }

        internal class UpperCaseBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(s.ToUpper(env.GetLocale()));
            }
        }

        internal class LowerCaseBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new SimpleScalar(s.ToLower(env.GetLocale()));
            }
        }

        internal class WordListBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                var result = new SimpleSequence();
                string[] words = s.Split();
                foreach (string word in words)
                {
                    result.Add(word);
                }
                return result;
            }
        }

        internal class EvalBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                var scs = new SimpleCharStream(
                        new StringReader("(" + s + ")"), Target.GetBeginLine(),
                        Target.GetBeginColumn(), s.Length + 2);
                var tokenSource = new FMParserTokenManager(scs);
                tokenSource.SwitchTo(FMParserConstants.FM_EXPRESSION);
                var parser = new FMParser(tokenSource)
                {
                    template = GetTemplate()
                };
                Expression exp;
                try
                {
                    exp = parser.Expression();
                }
                catch (ParseException pe)
                {
                    pe.SetTemplateName(GetTemplate().GetName());
                    throw new TemplateException(pe, env);
                }
                return exp.GetAsTemplateModel(env);
            }
        }

        internal class NumberBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                //try {
                //    return new SimpleNumber(env.GetArithmeticEngine().toNumber(s));
                //}
                //catch(NumberFormatException nfe) {
                //    string mess = "Error: " + getStartLocation()
                //                 + "\nExpecting a number here, found: " + s;
                //    throw new NonNumericalException(mess, env);
                //}

                throw new NotImplementedException();
            }
        }

        internal class SubstringBuiltIn : StringBuiltIn
        {
            protected override ITemplateModel CalculateResult(string s, Environment env)
            {
                return new BuiltInMethod(this, s);
            }

            private class BuiltInMethod : ITemplateMethodModelEx
            {
                private readonly SubstringBuiltIn _builtIn;
                private readonly string _s;

                public BuiltInMethod(SubstringBuiltIn builtIn, string s)
                {
                    _builtIn = builtIn;
                    _s = s;
                }

                public object Exec(IList<ITemplateModel> args)
                {
                    int argCount = args.Count, left, right = 0;
                    if (argCount != 1 && argCount != 2)
                    {
                        throw new TemplateModelException(
                            "Error: +getStartLocation() + \nExpecting 1 or 2 numerical arguments here");
                    }
                    try
                    {
                        var tnm = (ITemplateNumberModel) args[0];
                        left = tnm.GetAsNumber().IntValue();
                        if (argCount == 2)
                        {
                            tnm = (ITemplateNumberModel)args[1];
                            right = tnm.GetAsNumber().IntValue();
                        }
                    }
                    catch (InvalidCastException)
                    {
                        string mess = "Error: " + _builtIn.GetStartLocation() + "\nExpecting numerical argument here";
                        throw new TemplateModelException(mess);
                    }
                    return argCount == 1
                        ? new SimpleScalar(_s.Substring(left))
                        : new SimpleScalar(_s.Substring(left, right - left));
                }

                public object Exec(IList<string> arguments)
                {
                    throw new NotSupportedException();
                }
            }
        }
    }
}